// OpenGLView.cpp : implementation of the COpenGLView class
//

#include "stdafx.h"
#include "OpenGL.h"

#include "OpenGLDoc.h"
#include "OpenGLView.h"

#include "wrl.h"

#include <iostream>
using std::cout;
using std::endl;
#include "MaterialPropertiesDlg.h"
#include "LightDirectionDlg.h"
#include "MouseSensDlg.h"
#include "FogSetupDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#include "PngWrapper.h"


// For Status Bar access
#include "MainFrm.h"

// Use this macro to display text messages in the status bar.
#define STATUS_BAR_TEXT(str) (((CMainFrame*)GetParentFrame())->getStatusBar().SetWindowText(str))


/////////////////////////////////////////////////////////////////////////////
// COpenGLView

IMPLEMENT_DYNCREATE(COpenGLView, CView)

BEGIN_MESSAGE_MAP(COpenGLView, CView)
	//{{AFX_MSG_MAP(COpenGLView)
	ON_WM_ERASEBKGND()
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_WM_SIZE()
	ON_COMMAND(ID_FILE_LOAD, OnFileLoad)
	ON_COMMAND(ID_VIEW_ORTHOGRAPHIC, OnViewOrthographic)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ORTHOGRAPHIC, OnUpdateViewOrthographic)
	ON_COMMAND(ID_VIEW_PERSPECTIVE, OnViewPerspective)
	ON_UPDATE_COMMAND_UI(ID_VIEW_PERSPECTIVE, OnUpdateViewPerspective)
	ON_COMMAND(ID_ACTION_ROTATE, OnActionRotate)
	ON_UPDATE_COMMAND_UI(ID_ACTION_ROTATE, OnUpdateActionRotate)
	ON_COMMAND(ID_ACTION_SCALE, OnActionScale)
	ON_UPDATE_COMMAND_UI(ID_ACTION_SCALE, OnUpdateActionScale)
	ON_COMMAND(ID_ACTION_TRANSLATE, OnActionTranslate)
	ON_UPDATE_COMMAND_UI(ID_ACTION_TRANSLATE, OnUpdateActionTranslate)
	ON_COMMAND(ID_AXIS_X, OnAxisX)
	ON_UPDATE_COMMAND_UI(ID_AXIS_X, OnUpdateAxisX)
	ON_COMMAND(ID_AXIS_Y, OnAxisY)
	ON_UPDATE_COMMAND_UI(ID_AXIS_Y, OnUpdateAxisY)
	ON_COMMAND(ID_AXIS_Z, OnAxisZ)
	ON_UPDATE_COMMAND_UI(ID_AXIS_Z, OnUpdateAxisZ)
	ON_COMMAND(ID_OPTIONS_BACKFACECULLING, OnOptionsBackfaceculling)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_BACKFACECULLING, OnUpdateOptionsBackfaceculling)
	ON_COMMAND(ID_LIGHT_SHADING_FLAT, OnLightShadingFlat)
	ON_UPDATE_COMMAND_UI(ID_LIGHT_SHADING_FLAT, OnUpdateLightShadingFlat)
	ON_COMMAND(ID_LIGHT_SHADING_GOURAUD, OnLightShadingGouraud)
	ON_UPDATE_COMMAND_UI(ID_LIGHT_SHADING_GOURAUD, OnUpdateLightShadingGouraud)
	ON_COMMAND(ID_LIGHT_CONSTANTS, OnLightConstants)
	ON_COMMAND(ID_LIGHT_DIRECTION, OnLightDirection)
	ON_COMMAND(ID_BUTTON_SUBDIVIDE, OnButtonSubdivide)
	//}}AFX_MSG_MAP
	ON_COMMAND(ID_MODE_WIREFRAME, &COpenGLView::OnModeWireframe)
	ON_UPDATE_COMMAND_UI(ID_MODE_WIREFRAME, &COpenGLView::OnUpdateModeWireframe)
	ON_COMMAND(ID_MODE_FULL, &COpenGLView::OnModeFull)
	ON_UPDATE_COMMAND_UI(ID_MODE_FULL, &COpenGLView::OnUpdateModeFull)
	
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_COMMAND(ID_SPACE_OBJECT, &COpenGLView::OnSpaceObject)
	ON_UPDATE_COMMAND_UI(ID_SPACE_OBJECT, &COpenGLView::OnUpdateSpaceObject)
	ON_COMMAND(ID_SPACE_WORLD, &COpenGLView::OnSpaceWorld)
	ON_UPDATE_COMMAND_UI(ID_SPACE_WORLD, &COpenGLView::OnUpdateSpaceWorld)
	ON_COMMAND(ID_NORMALS_FACE, &COpenGLView::OnNormalsFace)
	ON_COMMAND(ID_NORMALS_VERTEX, &COpenGLView::OnNormalsVertex)
	ON_UPDATE_COMMAND_UI(ID_NORMALS_FACE, &COpenGLView::OnUpdateNormalsFace)
	ON_UPDATE_COMMAND_UI(ID_NORMALS_VERTEX, &COpenGLView::OnUpdateNormalsVertex)
	ON_COMMAND(ID_OPTIONS_BOUNDINGBOX, &COpenGLView::OnOptionsBoundingbox)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_BOUNDINGBOX, &COpenGLView::OnUpdateOptionsBoundingbox)
	ON_COMMAND(ID_OPTIONS_ANTI, &COpenGLView::OnOptionsAnti)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ANTI, &COpenGLView::OnUpdateOptionsAnti)
	ON_COMMAND(ID_OPTIONS_BACKGROUNDIMAGE, &COpenGLView::OnOptionsBackgroundimage)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_BACKGROUNDIMAGE, &COpenGLView::OnUpdateOptionsBackgroundimage)
	ON_COMMAND(ID_FILE_SAVE, &COpenGLView::OnFileSave)
	ON_COMMAND(ID_OPTIONS_MOUSESENSITIVITY, &COpenGLView::OnOptionsMousesensitivity)
	ON_COMMAND(ID_LIGHT_FOGSETTING, &COpenGLView::OnLightFogsetting)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COpenGLView construction/destruction

COpenGLView::COpenGLView()
{
	// Set default values
	m_nAxis = ID_AXIS_X;
	m_nAction = ID_ACTION_ROTATE;
	m_nView = ID_VIEW_ORTHOGRAPHIC;
	m_bBackFaceCulling = false;
	m_bIsPerspective = false;

	m_nLightShading = ID_LIGHT_SHADING_FLAT;
	m_nViewMode = ID_MODE_WIREFRAME;

	m_lMaterialAmbient = 0.2;
	m_lMaterialDiffuse = 0.8;
	m_lMaterialSpecular = 1.0;
	m_nMaterialCosineFactor = 32;

	m_nArrLightDirection[0] = 0;	// Light in direction of 0,0,1 into the screen
	m_nArrLightDirection[1] = 0;
	m_nArrLightDirection[2] = 1;

	m_lightsDlg.m_light1x = 0;
	m_lightsDlg.m_light1y = 0;
	m_lightsDlg.m_light1z = 1;
	m_lightsDlg.m_light1r = 255;
	m_lightsDlg.m_light1g = 255;
	m_lightsDlg.m_light1b = 255;
	m_lightsDlg.m_light1enable = TRUE;
	m_lightsDlg.m_ambient_intensitiy_r = 255;
	m_lightsDlg.m_ambient_intensitiy_g = 255;
	m_lightsDlg.m_ambient_intensitiy_b = 255;

	m_root = NULL;
	m_transformedShapes = NULL;
	m_numShapes = 0;

	m_bIsObjectSpace = false;
	m_dbMouseSensetivity = 1.4 ;
	m_bDrawBoundingBox = false;

	m_bDrawFaceNormals = false;
	m_bDrawVertexNormals = false;
	
	m_bSaveToFile = false;

	m_FogFilter = 0;
	
	m_bAntiAliasing = false;
	m_bBackgroundImage = false;

	m_fogMode = 2;
	m_fogColor.r = 0.5;
	m_fogColor.g = 0.5;
	m_fogColor.b = 0.5;
	m_fogDensity = 0.05;
	m_fogStart = 1;
	m_fogEnd = 2;
	m_fogEnable = false;
}

COpenGLView::~COpenGLView()
{
	FreeData();
}


/////////////////////////////////////////////////////////////////////////////
// COpenGLView diagnostics

#ifdef _DEBUG
void COpenGLView::AssertValid() const
{
	CView::AssertValid();
}

void COpenGLView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

COpenGLDoc* COpenGLView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(COpenGLDoc)));
	return (COpenGLDoc*)m_pDocument;
}
#endif //_DEBUG


/////////////////////////////////////////////////////////////////////////////
// COpenGLView Window Creation - Linkage of windows to OpenGL

BOOL COpenGLView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

    // An OpenGL window must be created with the following
    // flags and must NOT include CS_PARENTDC for the
    // class style.

    cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

	return CView::PreCreateWindow(cs);
}



int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;
	
    InitializeOpenGL();

	return 0;
}


// This method initialized the OpenGL system.
BOOL COpenGLView::InitializeOpenGL()
{
    m_pDC = new CClientDC(this);

    if ( NULL == m_pDC ) { // failure to get DC
	    ::AfxMessageBox("Couldn't get a valid DC.");
        return FALSE;
	}

    if ( !SetupPixelFormat() ) {
		::AfxMessageBox("SetupPixelFormat failed.\n");
        return FALSE;
	}

    if ( 0 == (m_hRC = ::wglCreateContext( m_pDC->GetSafeHdc() ) ) ) {
		::AfxMessageBox("wglCreateContext failed.");
        return FALSE;
	}

    if ( FALSE == ::wglMakeCurrent( m_pDC->GetSafeHdc(), m_hRC ) ) {
	    ::AfxMessageBox("wglMakeCurrent failed.");
        return FALSE;
	}

    // specify black as clear color
    ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
    // specify the back of the buffer as clear depth (0 closest, 1 farmost)
    ::glClearDepth( 1.0f );
    // enable depth testing (Enable zbuffer - hidden surface removal)
    ::glEnable( GL_DEPTH_TEST );
	// Set default black as background color.
	::glClearColor(0.0, 0.0, 0.0, 1.0f);

	// normally ours
	glEnable(GL_NORMALIZE);

	if (m_nLightShading == ID_LIGHT_SHADING_FLAT)
		glShadeModel(GL_FLAT);
	else
		glShadeModel(GL_SMOOTH);

	//glEnable(GL_LIGHTING);
	//glEnable( GL_LIGHT0 );
	glEnable(GL_LIGHTING);
	glEnable( GL_COLOR_MATERIAL );
	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

	GLfloat zeroVec[4] = { 0, 0, 0, 1.0 };
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroVec);

	//if (m_bBackFaceCulling) {
	//	glEnable(GL_CULL_FACE);
		//glCullFace(GL_BACK);
	//}
	//else 
	//	glDisable(GL_CULL_FACE);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

    return TRUE;
}



BOOL COpenGLView::SetupPixelFormat(PIXELFORMATDESCRIPTOR* pPFD)
{
    // default pixel format for a single-buffered,
    // OpenGL-supporting, hardware-accelerated, 
    // RGBA-mode format. Pass in a pointer to a different
    // pixel format if you want something else
    PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR),// size of this pfd
        1,                      // version number
        PFD_DRAW_TO_WINDOW |    // support window
          PFD_SUPPORT_OPENGL |  // support OpenGL
		  PFD_DOUBLEBUFFER,     // double buffered
        PFD_TYPE_RGBA,          // RGBA type
        24,                     // 24-bit color depth
        0, 0, 0, 0, 0, 0,       // color bits ignored
        0,                      // no alpha buffer
        0,                      // shift bit ignored
        0,                      // no accumulation buffer
        0, 0, 0, 0,             // accum bits ignored
        16,                     // 16-bit z-buffer
        0,                      // no stencil buffer
        0,                      // no auxiliary buffer
        PFD_MAIN_PLANE,         // main layer
        0,                      // reserved
        0, 0, 0                 // layer masks ignored
	};

    int pixelformat;
    PIXELFORMATDESCRIPTOR* pPFDtoUse;

    // let the user override the default pixel format
    pPFDtoUse = (0 == pPFD)? &pfd : pPFD; 
 
    if ( 0 == (pixelformat = ::ChoosePixelFormat( m_pDC->GetSafeHdc(), pPFDtoUse )) ) {
	    ::AfxMessageBox("ChoosePixelFormat failed.");
	    return FALSE;
    }

    if ( FALSE == ::SetPixelFormat(m_pDC->GetSafeHdc(), pixelformat, pPFDtoUse ) ) {
	    ::AfxMessageBox("SetPixelFormat failed.");
        return FALSE;
    }

    return TRUE;
}



/////////////////////////////////////////////////////////////////////////////
// COpenGLView message handlers


void COpenGLView::OnSize(UINT nType, int cx, int cy) 
{
	CView::OnSize(nType, cx, cy);

	if ( 0 >= cx || 0 >= cy ) {
		return;
	}

	// save the width and height of the current window
	m_WindowWidth = cx;
	m_WindowHeight = cy;

	// compute the aspect ratio
	// this will keep all dimension scales equal
	m_AspectRatio = (GLdouble)m_WindowWidth/(GLdouble)m_WindowHeight;

	// Now, set up the viewing area-select the full client area
	::glViewport(0, 0, m_WindowWidth, m_WindowHeight);

	if ( GL_NO_ERROR != ::glGetError() ) {
		//do nothing
	}

	SetView();

	::glMatrixMode(GL_MODELVIEW);
}


BOOL COpenGLView::SetupViewingFrustum(void)
{
    // select a default perspective viewing volumn
    ::gluPerspective( 40.0f, m_AspectRatio, 0.1f, 20.0f );

    // NOTE: Other commands you could have here are
    // glFrustum, which gives you much more control over
    // the perspective view, or glOrtho which is used for
    // parallel projections. No matter what you use, check
    // the error status when you set the viewing frustum!

    if ( GL_NO_ERROR != ::glGetError() ) {
	    ::AfxMessageBox("Error while trying to set viewing frustum.");
	    return FALSE;
    }

    return TRUE;
}


BOOL COpenGLView::SetupViewingOrtho(void)
{
    // select a default perspective viewing volumn
    ::glOrtho( -2, 2, -2, 2, -2, 2);
	// This gives a projection around the (-2,-2,-2), (2,2,2) coordinate.
	// The fifth and six parameter are near and far. Since it is used as
	// -near and -far, than this gives us the actual near (positive z) to be 2
	// and far (nefative z) to be -2.
	// Note that the view point is from (0,0,0) (yes, inside) towards the negative z.

    // NOTE: Other commands you could have here are
    // glFrustum, which gives you much more control over
    // the perspective view, or glOrtho which is used for
    // parallel projections. No matter what you use, check
    // the error status when you set the viewing frustum!

    if ( GL_NO_ERROR != ::glGetError() ) {
	    ::AfxMessageBox("Error while trying to set viewing frustum.");
	    return FALSE;
    }

    return TRUE;
}


// This viewing projection gives us a constant aspect ration. This is done by
// increasing the corresponding size of the ortho cube.
BOOL COpenGLView::SetupViewingOrthoConstAspect(void)
{
	double windowSize = 200;	// the size of the window in GL coord system.


	if ( m_AspectRatio > 1 ) {	// x is bigger than y.
		// Maintain y size and increase x size (and z) accordingly
		// by MULTIPLYING by the aspect ration.
	    ::glOrtho(  -windowSize*m_AspectRatio/2.0, windowSize*m_AspectRatio/2.0,
					-windowSize/2.0, windowSize/2.0,
					-windowSize*m_AspectRatio/2.0, windowSize*m_AspectRatio/2.0);
	} else {
		// Maintain x size and increase y size (and z) accordingly, 
		// by DIVIDING the aspect Ration (because it's smaller than 1).
	    ::glOrtho(  -windowSize/2.0, windowSize/2.0,
					-windowSize/m_AspectRatio/2.0, windowSize/m_AspectRatio/2.0,
					-windowSize/m_AspectRatio/2.0, windowSize/m_AspectRatio/2.0);
	}

    if ( GL_NO_ERROR != ::glGetError() ) {
	    ::AfxMessageBox("Error while trying to set viewing frustum.");
	    return FALSE;
    }

    return TRUE;
}





BOOL COpenGLView::OnEraseBkgnd(CDC* pDC) 
{
	// Windows will clear the window with the background color every time your window 
	// is redrawn, and then OpenGL will clear the viewport with its own background color.

	// return CView::OnEraseBkgnd(pDC);
	return true;
}



/////////////////////////////////////////////////////////////////////////////
// COpenGLView drawing
/////////////////////////////////////////////////////////////////////////////

void COpenGLView::OnDraw(CDC* pDC)
{
	COpenGLDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// clear screen and zbuffer

	glMatrixMode(GL_MODELVIEW);

     
	//GLfloat oldfMatrix[16];        
	//glGetFloatv(GL_MODELVIEW_MATRIX,oldfMatrix);        
	
	//glLoadIdentity();         
	//glMultMatrixf(oldfMatrix);
	//DoResizingBeforeTransformation();

	// draw just the axis
	//glPushMatrix();
	//	draw_axis();
	//glPopMatrix();
	
	RenderScene();
	//DrawFog();
	glFlush();
	SwapBuffers(wglGetCurrentDC());

	//glLoadIdentity();
	//glMultMatrixf(oldfMatrix);
}


/////////////////////////////////////////////////////////////////////////////
// COpenGLView OpenGL Finishing and clearing...

void COpenGLView::OnDestroy() 
{
	CView::OnDestroy();
	
    // this call makes the current RC not current
    if ( FALSE ==  ::wglMakeCurrent( 0, 0 ) ) {
        ::AfxMessageBox("wglMakeCurrent failed.");
	}

    // delete the RC
    if ( m_hRC && (FALSE == ::wglDeleteContext( m_hRC )) ) {
        ::AfxMessageBox("wglDeleteContext failed.");
	}

    // delete the DC
    if ( m_pDC ) {
        delete m_pDC;
	}
}



/////////////////////////////////////////////////////////////////////////////
// User Defined Functions


void COpenGLView::draw_axis()
{
	glLineWidth(2.0f);
	glColor3f(1.0f, 1.0f, 1.0f);
	
	// Draw Axis x
	glColor3f(1, 0, 0);
	glBegin(GL_LINES);
		glVertex3d(-0.1,0,0);
		glVertex3d(   1,0,0);
	glEnd();
	glPushMatrix();
		glTranslatef( 1.0f, 0.0f, 0.0f );
		glRotatef( 90.0, 0.0f, 1.0f, 0.0f );
		auxSolidCone(0.05,0.5);
	glPopMatrix();

	
	glColor3f(0, 1, 0);
	glBegin(GL_LINES);
		glVertex3d(0,-0.1,0);
		glVertex3d(0,   1,0);
	glEnd();
	glPushMatrix();
		glTranslatef( 0.0f, 1.0f, 0.0f );
		glRotatef( -90.0, 1.0f, 0.0f, 0.0f );
		auxSolidCone(0.05,0.5);
	glPopMatrix();

	
	glColor3f(0, 0, 1);
	glBegin(GL_LINES);
		glVertex3d(0,0,-0.1);
		glVertex3d(0,0,   1);
	glEnd();
	glPushMatrix();
		glTranslatef( 0.0f, 0.0f, 1.0f );
		auxSolidCone(0.05,0.5);
	glPopMatrix();

	glLineWidth(1);			// return defaul line width


	glColor3f(1,1,1);		// return default color
}


void COpenGLView::RenderScene() {
	SetLights();

	//	Load background image
	if (m_bBackgroundImage)
	{
		glDrawBuffer(GL_BACK);

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();

		PngWrapper pngRead(m_strBackImage);
		pngRead.ReadPng();

		int width = pngRead.GetWidth();
		int height = pngRead.GetHeight();

		glOrtho(0.0, (GLdouble)width, 0.0, (GLdouble)height, 200000, -200000);

		GLubyte *buff = new GLubyte[3 * width];

		for (int i = 0; i < height; i++)
		{

			for (int j = 0; j < width; j++)
			{
				int color = pngRead.GetValue(j, height - i - 1);

				buff[3 * j + 0] = (GLubyte)(GET_R(color)) ;
				buff[3 * j + 1] = (GLubyte)(GET_G(color)) ;
				buff[3 * j + 2] = (GLubyte)(GET_B(color)) ;
			}

			glRasterPos3i(0,i, 100000);
			glDrawPixels(width, 1,GL_RGB, GL_UNSIGNED_BYTE, 
				(GLvoid *)buff);
		}

		//delete buff;
		buff = NULL;

		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
	}


	if (m_nViewMode == ID_MODE_WIREFRAME)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	else // full
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	for (unsigned i(0);i<m_numShapes;++i) {
		ASSERT(m_transformedShapes[i]->geometry->type == VR_INDEXED_FACE_SET);

		VRIndexedFaceSet* ifs = (VRIndexedFaceSet*)m_transformedShapes[i]->geometry;
		RenderIFS(ifs);
	}

	//	Writing to PNG file
	if (m_bSaveToFile)
	{
		glReadBuffer(GL_BACK);

        TCHAR szFilters[] = _T ("PNG Image Files (*.png)|*.png|All Files (*.*)|*.*||");

        CFileDialog fileNameDlg(FALSE, "png", "*.png", OFN_FILEMUSTEXIST | OFN_HIDEREADONLY ,szFilters);

        if (fileNameDlg.DoModal() == IDOK) 
		{
			CString strSaveRenderToFileName = fileNameDlg.GetPathName();    // get full file name and path
            int Width = m_WindowWidth;
            int Height = m_WindowHeight;
            GLubyte *pBuf = new GLubyte[4 * Width];
            PngWrapper pngWrite((char*)(LPCTSTR)strSaveRenderToFileName, Width, Height);
            pngWrite.InitWritePng();
            for (int i = 0; i < Height; i++)
            {
                    glReadPixels(0,i,Width, 1,GL_RGBA, GL_UNSIGNED_BYTE,(GLvoid *)pBuf);
                    for (int j = 0; j < Width; j++)
                    {
                            int red = (int)(pBuf[4 * j + 0]);
							int green = (int)(pBuf[4 * j + 1]);
							int blue = (int)(pBuf[4 * j + 2]);
                            pngWrite.SetValue(j,Height - i - 1,SET_RGB(red,green,blue));

                    }
            }
            pngWrite.WritePng();

            //delete pBuf;
            pBuf = NULL;

        }
        m_bSaveToFile = false;
	}

	if (m_bDrawBoundingBox)
		DrawBoundingBox();
}


void COpenGLView::OnFileLoad() 
{
	TCHAR szFilters[] = _T ("VRML Data Files (*.wrl)|*.wrl|All Files (*.*)|*.*||");

	CFileDialog dlg(TRUE, "wrl", "*.wrl", OFN_FILEMUSTEXIST | OFN_HIDEREADONLY ,szFilters);

	if (dlg.DoModal () == IDOK) {
		FreeData();

		m_strVrmlFileName = dlg.GetPathName();		// Full path and filename
		char *tmpFilename = (char*)(LPCTSTR)m_strVrmlFileName;
		m_root = Vrml2VR(tmpFilename);
		if (!m_root) {
			MessageBox("Could'nt parse vrml.");
			return;
		}

		m_root->transformShapes(&m_transformedShapes, m_numShapes, false);

		InitAll();
		SetView();
		Invalidate();	// force a WM_PAINT for drawing.
	} 
	
}





// VIEW HANDLERS ///////////////////////////////////////////

// Note: that all the following Message Handlers act in a similar way.
// Each control or command has two functions associated with it.

void COpenGLView::OnViewOrthographic() 
{
	m_nView = ID_VIEW_ORTHOGRAPHIC;
	m_bIsPerspective = false;
	SetView();
	Invalidate();		// redraw using the new view.
}

void COpenGLView::OnUpdateViewOrthographic(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nView == ID_VIEW_ORTHOGRAPHIC);
}

void COpenGLView::OnViewPerspective() 
{
	m_nView = ID_VIEW_PERSPECTIVE;
	m_bIsPerspective = true;
	SetView();
	Invalidate();
}

void COpenGLView::OnUpdateViewPerspective(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nView == ID_VIEW_PERSPECTIVE);
}




// ACTION HANDLERS ///////////////////////////////////////////

void COpenGLView::OnActionRotate() 
{
	m_nAction = ID_ACTION_ROTATE;
}

void COpenGLView::OnUpdateActionRotate(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nAction == ID_ACTION_ROTATE);
}

void COpenGLView::OnActionTranslate() 
{
	m_nAction = ID_ACTION_TRANSLATE;
}

void COpenGLView::OnUpdateActionTranslate(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nAction == ID_ACTION_TRANSLATE);
}

void COpenGLView::OnActionScale() 
{
	m_nAction = ID_ACTION_SCALE;
}

void COpenGLView::OnUpdateActionScale(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nAction == ID_ACTION_SCALE);
}




// AXIS HANDLERS ///////////////////////////////////////////


// Gets calles when the X button is pressed or when the Axis->X menu is selected.
// The only thing we do here is set the ChildView member variable m_nAxis to the 
// selected axis.
void COpenGLView::OnAxisX() 
{
	m_nAxis = ID_AXIS_X;
}

// Gets called when windows has to repaint either the X button or the Axis pop up menu.
// The control is responsible for its redrawing.
// It sets itself disabled when the action is a Scale action.
// It sets itself Checked if the current axis is the X axis.
void COpenGLView::OnUpdateAxisX(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(m_nAction != ID_ACTION_SCALE);
	pCmdUI->SetCheck(m_nAxis == ID_AXIS_X);
}


void COpenGLView::OnAxisY() 
{
	m_nAxis = ID_AXIS_Y;
}

void COpenGLView::OnUpdateAxisY(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(m_nAction != ID_ACTION_SCALE);
	pCmdUI->SetCheck(m_nAxis == ID_AXIS_Y);
}


void COpenGLView::OnAxisZ() 
{
	m_nAxis = ID_AXIS_Z;
}

void COpenGLView::OnUpdateAxisZ(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(m_nAction != ID_ACTION_SCALE);
	pCmdUI->SetCheck(m_nAxis == ID_AXIS_Z);
}





// OPTIONS HANDLERS ///////////////////////////////////////////

void COpenGLView::OnOptionsBackfaceculling() 
{
	m_bBackFaceCulling = (m_bBackFaceCulling ? false : true);

	if (m_bBackFaceCulling) {
		glEnable(GL_CULL_FACE);
		//glCullFace(GL_BACK);
	}
	else 
		glDisable(GL_CULL_FACE);

	Invalidate();
}

void COpenGLView::OnUpdateOptionsBackfaceculling(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bBackFaceCulling);
}




// LIGHT SHADING HANDLERS ///////////////////////////////////////////

void COpenGLView::OnLightShadingFlat() 
{
	m_nLightShading = ID_LIGHT_SHADING_FLAT;

	glShadeModel(GL_FLAT);
	glEnable(GL_FLAT);
	glDisable(GL_SMOOTH);
	Invalidate();
}

void COpenGLView::OnUpdateLightShadingFlat(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nLightShading == ID_LIGHT_SHADING_FLAT);
}


void COpenGLView::OnLightShadingGouraud() 
{
	m_nLightShading = ID_LIGHT_SHADING_GOURAUD;

	glShadeModel(GL_SMOOTH);
	glEnable(GL_SMOOTH);
	glDisable(GL_FLAT);
	Invalidate();
}

void COpenGLView::OnUpdateLightShadingGouraud(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_nLightShading == ID_LIGHT_SHADING_GOURAUD);
}



// LIGHT CONSTANTS HANDLER ///////////////////////////////////////////

void COpenGLView::OnLightConstants() 
{
	CMaterialPropertiesDlg dlg;
	// pass current values
	dlg.m_strAmbient.Format("%.2lf", m_lMaterialAmbient);
	dlg.m_strDiffuse.Format("%.2lf", m_lMaterialDiffuse);
	dlg.m_strSpecular.Format("%.2lf", m_lMaterialSpecular);
	dlg.m_nMaterialCosineFactor = m_nMaterialCosineFactor;

	if (dlg.DoModal() == IDOK) {
		// set new current values
		m_lMaterialAmbient = atof(dlg.m_strAmbient);
		m_lMaterialDiffuse = atof(dlg.m_strDiffuse);
		m_lMaterialSpecular = atof(dlg.m_strSpecular);
		m_nMaterialCosineFactor = dlg.m_nMaterialCosineFactor;
	}

	Invalidate();
}


// LIGHT DIRECTION HANDLER ///////////////////////////////////////////

void COpenGLView::OnLightDirection() 
{
	m_lightsDlg.DoModal();
	Invalidate();
	
 /*
	CLightDirectionDlg dlg;
	// pass current values
	dlg.m_nDirectionX = m_nArrLightDirection[0];
	dlg.m_nDirectionY = m_nArrLightDirection[1];
	dlg.m_nDirectionZ = m_nArrLightDirection[2];

	if (dlg.DoModal() == IDOK) {

		// set new current values
		m_nArrLightDirection[0] = dlg.m_nDirectionX;
		m_nArrLightDirection[1] = dlg.m_nDirectionY;
		m_nArrLightDirection[2] = dlg.m_nDirectionZ;

		
		//GLfloat light_pos[] = { dlg.m_nDirectionX, dlg.m_nDirectionY, dlg.m_nDirectionZ, 0};
		//glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
		//GLint col[] = {255,255,255};
		//glLightiv(GL_LIGHT0, GL_DIFFUSE, col);

		Invalidate();
	}
*/
}



// SUBDIVISION HANDLER ///////////////////////////////////////////

void COpenGLView::OnButtonSubdivide() 
{
	// TODO: Add your command handler code here
	
}

void COpenGLView::InitAll (void) {
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	CalcBoundingBox();
	CalcCenterOfMass();

	
}

void COpenGLView::DoResizingBeforeTransformation()
{
	/*
	glMatrixMode(GL_MODELVIEW);        
	GLfloat transfMatrix[16];        
	glGetFloatv(GL_MODELVIEW_MATRIX,transfMatrix);        
	glLoadIdentity();         
	glMultMatrixf(transfMatrix);
	*/
	double dbSize = 0;
	
	CRect rect;        
	GetWindowRect(&rect);        
	if(rect.Width()< rect.Height())
		dbSize = (rect.Width()+1) / (m_maxCoord.x - m_minCoord.x);
	else
		dbSize = (rect.Height()+1) / (m_maxCoord.y - m_minCoord.y);
			
	dbSize = (1.0/4.0)*dbSize;

	glScaled(dbSize,dbSize,dbSize);
	glTranslatef(-m_CenterOfMass.x,-m_CenterOfMass.y,-m_CenterOfMass.z);	

	/* 
	// perspective: DUNNO!
	double dx = m_maxCoord.x - m_minCoord.x;
	double dy = m_maxCoord.y - m_minCoord.y;
	double dz = m_maxCoord.z - m_minCoord.z; 
	double factor = 0.05 * sqrt(dx*dx + dy*dy + dz*dz); 

	glMatrixMode(GL_MODELVIEW);
	GLfloat transformTemp[16];        
	glGetFloatv(GL_MODELVIEW_MATRIX,transformTemp);
	glLoadIdentity();        
	if(!m_bIsPerspective)                 
		gluLookAt(0,0,0,0,0,-1,0,1,0); 
	else                 
		gluLookAt((m_minCoord.x + m_maxCoord.x)/2, (m_minCoord.y + m_maxCoord.y)/2, (m_maxCoord.z > 0)?m_maxCoord.z + 10 * factor:factor, (m_minCoord.x + m_maxCoord.x)/2, (m_minCoord.y + m_maxCoord.y)/2, m_minCoord.z, 0,1,0);
	glMultMatrixf(transformTemp);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();        
	if(!m_bIsPerspective)                 
		glOrtho(m_minCoord.x,m_maxCoord.x,m_minCoord.y,m_maxCoord.y,m_minCoord.z,m_maxCoord.z);
	else                
		gluPerspective(60,m_AspectRatio,(m_maxCoord.z > 0)?m_maxCoord.z :0 ,m_minCoord.z); 
	glMatrixMode(GL_MODELVIEW);
	*/
}

void COpenGLView::FreeData (void) {
	if (m_root) {
		m_root->releaseAuxiliaryMemory();
		m_root->release();
		m_root = NULL;
		m_numShapes = 0;
		m_transformedShapes = NULL;
		
		m_faceColors.clear();
		m_vertexColors.clear();
		m_faceNormals.clear();
		m_vertexNormals.clear();
	}
}

void COpenGLView::OnModeWireframe()
{
	m_nViewMode = ID_MODE_WIREFRAME;
	Invalidate();
}

void COpenGLView::OnUpdateModeWireframe(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_nViewMode == ID_MODE_WIREFRAME);
}

void COpenGLView::OnModeFull()
{
	m_nViewMode = ID_MODE_FULL;
	Invalidate();
}

void COpenGLView::OnUpdateModeFull(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_nViewMode == ID_MODE_FULL);
}

void COpenGLView::RenderIFS (VRIndexedFaceSet* ifs) {
	// -----------------------------------
	// experimental:
		//glFrontFace(GL_CCW);
		//glFrontFace(GL_CW);
	glFrontFace(GL_CCW);
	// -----------------------------------

	vector<MyColor> &faceColors(GetFaceColors(ifs));
	vector<MyColor> &vertexColors(GetVertexColors(ifs));
	vector<MyNormal> &faceNormals(GetFaceNormals(ifs));
	vector<MyNormal> &vertexNormals(GetVertexNormals(ifs));

	VRShape* parent = (VRShape*)ifs->parent;
	double AmbientIntensity = parent->appearance && parent->appearance->material ?
		parent->appearance->material->ambientIntensity : 1;
	double Shiney = parent->appearance && parent->appearance->material ? 
		parent->appearance->material->shininess : 32;
	MyVector Kd(parent->appearance && parent->appearance->material ? parent->appearance->material->diffuseColor[0] : 1,
		parent->appearance && parent->appearance->material ? parent->appearance->material->diffuseColor[1] : 1,
		parent->appearance && parent->appearance->material ? parent->appearance->material->diffuseColor[2] : 1);
	MyVector Ks(parent->appearance && parent->appearance->material ? parent->appearance->material->specularColor[0] : 1,
		parent->appearance && parent->appearance->material ? parent->appearance->material->specularColor[1] : 1,
		parent->appearance && parent->appearance->material ? parent->appearance->material->specularColor[2] : 1);
	MyVector Ka(Kd.x*AmbientIntensity, Kd.y*AmbientIntensity, Kd.z*AmbientIntensity);

	//GLfloat defColorAmb [] = {Ka.x*m_lMaterialAmbient,Ka.y*m_lMaterialAmbient, Ka.z*m_lMaterialAmbient, 1} ;
	//GLfloat defColorDif [] = {Kd.x*m_lMaterialDiffuse,Kd.y*m_lMaterialDiffuse,Kd.z*m_lMaterialDiffuse, 1} ;
	//GLfloat defColorSpec [] = {Ks.x*m_lMaterialSpecular,Ks.y*m_lMaterialSpecular,Ks.z*m_lMaterialSpecular, 1};
	//GLfloat shineines [] = { (GLfloat)m_nMaterialCosineFactor*Shiney};

	GLfloat defColorAmb [] = {Ka.x,Ka.y, Ka.z, 1} ;
	GLfloat defColorDif [] = {Kd.x,Kd.y,Kd.z, 1} ;
	GLfloat defColorSpec [] = {Ks.x,Ks.y,Ks.z, 1};
	GLfloat shineines [] = { (GLfloat)m_nMaterialCosineFactor};


	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, defColorAmb);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, defColorDif);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defColorSpec);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shineines);

	unsigned j(0), poly(0);
	int idx;
	while (j<ifs->coordIndex.numValues) {
		glBegin(GL_POLYGON);
		// face color:
		if (faceColors.size())
		{
			glColor4f(faceColors[poly].r, faceColors[poly].g, faceColors[poly].b,1.0);
		}
		else {
			glColor4f(Kd.x,Kd.y,Kd.z,1.0);
		}
		// face normal:
		if (m_nLightShading == ID_LIGHT_SHADING_FLAT && faceNormals.size())
			glNormal3f(faceNormals[poly].x, faceNormals[poly].y, faceNormals[poly].z);
		while ((idx = ifs->coordIndex.values[j++]) != -1) {
			// vertex color:
			if (vertexColors.size())
				glColor4f(vertexColors[idx].r, vertexColors[idx].g, vertexColors[idx].b,1.0);
			// vertex normal:
			if (m_nLightShading == ID_LIGHT_SHADING_GOURAUD && vertexNormals.size())
				glNormal3f(vertexNormals[idx].x, vertexNormals[idx].y, vertexNormals[idx].z);
			// draw vertex:
			glVertex3f(ifs->coord->point.values[idx][0], ifs->coord->point.values[idx][1], ifs->coord->point.values[idx][2]);
		}
		glEnd();
		++poly;
	}
	
	if (m_bDrawFaceNormals)
		DrawFaceNormals(ifs);

	if (m_bDrawVertexNormals)
		DrawVertexNormals(ifs);
}

vector<COpenGLView::MyColor> &COpenGLView::GetFaceColors (VRIndexedFaceSet* ifs) {
	map<VRIndexedFaceSet*, vector<MyColor> >::iterator it = m_faceColors.find(ifs);
	if (it != m_faceColors.end())
		return it->second;

	// else we have to fill it
	if(ifs->colorPerVertex == false && ifs->color && ifs->color->color.numValues>0) {
		VRMFInt32 &index = (ifs->colorIndex.numValues == 0) ? ifs->coordIndex : ifs->colorIndex;  

		vector<MyColor> &curr(m_faceColors[ifs]);
		curr.resize(index.numValues);

		for (unsigned int j = 0; j < index.numValues; j++) 
		{                        
			int idx_color = index.values[j];                        
			if (idx_color < 0)
				continue;
			curr[j].r = ifs->color->color.values[idx_color][0];
			curr[j].g = ifs->color->color.values[idx_color][1];
			curr[j].b = ifs->color->color.values[idx_color][2];
		}
		
		return curr;
	}

	static vector<MyColor> empty;
	return empty;
}

vector<COpenGLView::MyColor> &COpenGLView::GetVertexColors (VRIndexedFaceSet* ifs) {
	map<VRIndexedFaceSet*, vector<MyColor> >::iterator it = m_vertexColors.find(ifs);
	if (it != m_vertexColors.end())
		return it->second;

	if(ifs->colorPerVertex && ifs->color && ifs->color->color.numValues>0) {
		VRMFInt32 &index = (ifs->colorIndex.numValues == 0) ? ifs->coordIndex : ifs->colorIndex;

		vector<MyColor> &curr(m_vertexColors[ifs]);
		curr.resize(ifs->coord->point.numValues);

		for (unsigned int j = 0; j < index.numValues; j++) {                        		
			int idx_color = index.values[0];                        
			int idx_point = ifs->coordIndex.values[j];
			if (idx_color < 0 || idx_point < 0)
				continue;
			
			curr[idx_point].r = ifs->color->color.values[idx_color][0];
			curr[idx_point].g = ifs->color->color.values[idx_color][1];
			curr[idx_point].b = ifs->color->color.values[idx_color][2];
		}

		return curr;
	}

	static vector<MyColor> empty;
	return empty;
}

void COpenGLView::CalcBoundingBox (void) {
	for (unsigned i(0);i<m_numShapes;++i) {
		ASSERT(m_transformedShapes[i]->geometry->type == VR_INDEXED_FACE_SET);

		VRIndexedFaceSet* ifs = (VRIndexedFaceSet*)m_transformedShapes[i]->geometry;

		for (unsigned j(0);j<ifs->coord->point.numValues;++j) {
			m_minCoord.x = (!i && !j) ? ifs->coord->point.values[j][0] : min(ifs->coord->point.values[j][0], m_minCoord.x);
			m_maxCoord.x = (!i && !j) ? ifs->coord->point.values[j][0] : max(ifs->coord->point.values[j][0], m_maxCoord.x);
			m_minCoord.y = (!i && !j) ? ifs->coord->point.values[j][1] : min(ifs->coord->point.values[j][1], m_minCoord.y);
			m_maxCoord.y = (!i && !j) ? ifs->coord->point.values[j][1] : max(ifs->coord->point.values[j][1], m_maxCoord.y);
			m_minCoord.z = (!i && !j) ? ifs->coord->point.values[j][2] : min(ifs->coord->point.values[j][2], m_minCoord.z);
			m_maxCoord.z = (!i && !j) ? ifs->coord->point.values[j][2] : max(ifs->coord->point.values[j][2], m_maxCoord.z);
		}

	}
}

void COpenGLView::CalcCenterOfMass (void) 
{
	m_CenterOfMass.x = 0;
	m_CenterOfMass.y = 0;
	m_CenterOfMass.z = 0;
	unsigned tot(0);
	for (unsigned i(0);i<m_numShapes;++i) 
	{
		ASSERT(m_transformedShapes[i]->geometry->type == VR_INDEXED_FACE_SET);

		VRIndexedFaceSet* ifs = (VRIndexedFaceSet*)m_transformedShapes[i]->geometry;
		for (unsigned j(0);j<ifs->coord->point.numValues;++j) {
			++tot;
			m_CenterOfMass.x += ifs->coord->point.values[j][0];
			m_CenterOfMass.y += ifs->coord->point.values[j][1];
			m_CenterOfMass.z += ifs->coord->point.values[j][2];
		}
	}
	m_CenterOfMass.x /= tot;
	m_CenterOfMass.y /= tot;
	m_CenterOfMass.z /= tot;
}

vector<COpenGLView::MyNormal> &COpenGLView::GetFaceNormals (VRIndexedFaceSet* ifs) {
	map<VRIndexedFaceSet*, vector<MyNormal> >::iterator it = m_faceNormals.find(ifs);
	if (it != m_faceNormals.end())
		return it->second;

	if(ifs->normalPerVertex == false && ifs->normal && ifs->normal->vector.numValues > 0) {
		VRMFInt32 &index = (ifs->normalIndex.numValues == 0)? ifs->coordIndex : ifs->normalIndex;

		vector<MyNormal> &curr(m_faceNormals[ifs]);
		curr.resize(CalcTotalFaces(ifs));

		for (unsigned int j = 0; j < index.numValues; j++) {                        
			int idx_normal = index.values[j];                        
			int idx_face = ifs->coordIndex.values[j];
			if (idx_normal < 0 || idx_face < 0 || idx_face >= curr.size())
				continue;

			curr[idx_face].x = ifs->normal->vector.values[idx_normal][0];
			curr[idx_face].y = ifs->normal->vector.values[idx_normal][1];
			curr[idx_face].z = ifs->normal->vector.values[idx_normal][2];

			// needed if we want to draw them: 
			curr[idx_face].normalize();
		}

		return curr;
	}
	
	vector<MyNormal> &curr(m_faceNormals[ifs]);
	CalcFaceNormals(ifs, curr);
	return curr;
}

vector<COpenGLView::MyNormal> &COpenGLView::GetVertexNormals (VRIndexedFaceSet* ifs) {
	map<VRIndexedFaceSet*, vector<MyNormal> >::iterator it = m_vertexNormals.find(ifs);
	if (it != m_vertexNormals.end())
		return it->second;

	if(ifs->normalPerVertex && ifs->normal && ifs->normal->vector.numValues > 0) {
		VRMFInt32 &index = (ifs->normalIndex.numValues == 0)? ifs->coordIndex : ifs->normalIndex;

		vector<MyNormal> &curr(m_vertexNormals[ifs]);
		curr.resize(ifs->coord->point.numValues);

		for (unsigned int j = 0; j < index.numValues; j++) {                        
			int idx_normal = index.values[j];                        
			int idx_point = ifs->coordIndex.values[j];
			if (idx_normal < 0 || idx_point < 0)
				continue;

			curr[idx_point].x = ifs->normal->vector.values[idx_normal][0];
			curr[idx_point].y = ifs->normal->vector.values[idx_normal][1];
			curr[idx_point].z = ifs->normal->vector.values[idx_normal][2];

			// needed if we want to draw them: 
			curr[idx_point].normalize();
		}

		return curr;
	}

	vector<MyNormal> &curr(m_vertexNormals[ifs]);
	CalcVertexNormals(ifs, curr);
	return curr;
}

unsigned COpenGLView::CalcTotalFaces (VRIndexedFaceSet* ifs) {
	unsigned total = 0;
	for (unsigned i(0);i<ifs->coordIndex.numValues;++i)
		if (ifs->coordIndex.values[i] == -1)
			++total;
	return total;
}

void COpenGLView::CalcVertexNormals (VRIndexedFaceSet* ifs, vector<COpenGLView::MyNormal> &normals) {
	vector<MyNormal> &facenormals(GetFaceNormals(ifs));
	normals.clear();
	normals.resize(ifs->coord->point.numValues);

	unsigned i(0), currface(0);
	while (i<ifs->coordIndex.numValues) {
		int idx;
		while ((idx = ifs->coordIndex.values[i++]) != -1) {
			normals[idx].x += facenormals[currface].x;
			normals[idx].y += facenormals[currface].y;
			normals[idx].z += facenormals[currface].z;
		}
		++currface;
	}

	for (vector<COpenGLView::MyNormal>::iterator it(normals.begin());it!=normals.end();++it)
		it->normalize();
}

void COpenGLView::CalcFaceNormals (VRIndexedFaceSet* ifs, vector<COpenGLView::MyNormal> &normals) {
	normals.clear();
	normals.resize(CalcTotalFaces(ifs));
	
	// IMPORTANT: we make an assumption that each face consists of at least 3 points.

	unsigned i(0), currface(0);
	while (i<ifs->coordIndex.numValues) {
		int pnt0(ifs->coordIndex.values[i]);
		int pnt1(ifs->coordIndex.values[i+1]); 
		int pnt2(ifs->coordIndex.values[i+2]);

		MyVector line1(ifs->coord->point.values[pnt1][0] - ifs->coord->point.values[pnt0][0],
						ifs->coord->point.values[pnt1][1] - ifs->coord->point.values[pnt0][1],
						ifs->coord->point.values[pnt1][2] - ifs->coord->point.values[pnt0][2]);
		MyVector line2(ifs->coord->point.values[pnt2][0] - ifs->coord->point.values[pnt1][0],
						ifs->coord->point.values[pnt2][1] - ifs->coord->point.values[pnt1][1],
						ifs->coord->point.values[pnt2][2] - ifs->coord->point.values[pnt1][2]);
		
		MyVector cross = line1.cross(line2);

		normals[currface].x = cross.x;
		normals[currface].y = cross.y;
		normals[currface].z = cross.z;
		normals[currface].normalize();

		while (ifs->coordIndex.values[i++] != -1);
		++currface;
	}
}

void COpenGLView::OnLButtonDown(UINT nFlags, CPoint point) 
{        
	SetCapture();   // capture the mouse 'left button up' command
	m_nXCoord = point.x;            // keep x coordinate        
	m_nYCoord = point.y;            // keep y coordinate 
}  

void COpenGLView::OnLButtonUp(UINT nFlags, CPoint point)
{  
	if (GetCapture() == this) ::ReleaseCapture(); 
}  

void COpenGLView::OnMouseMove(UINT nFlags, CPoint point) 
{        
	CString str;                    
	// create and prepare string        
	CMainFrame* pMF = (CMainFrame*)GetParentFrame();        
	// get Parent Main Frame        
	if (GetCapture() == this) 
	{             
		// 'this' has the mouse capture                 
		// This is the movement ammount                
		str.Format("X,Y: %d,%d", point.x - m_nXCoord, point.y - m_nYCoord);                
		pMF->getStatusBar().SetPaneText(0,str);         
		// get the Status Bar and place text                 
		switch(m_nAction) 
		{                     
			// act based on the current action                
			case ID_ACTION_ROTATE:          
				rotate(point.x - m_nXCoord);    
				break;                
			case ID_ACTION_TRANSLATE:       
				translate(point.x - m_nXCoord);    
				break;                
			case ID_ACTION_SCALE:           
				scale(point.x - m_nXCoord);             
				break;                
		}                
		Invalidate();                                   
		// redraw scene                
		m_nXCoord = point.x;                    
		// keep new point                
		m_nYCoord = point.y;                    
		// keep new point        
	} 
	else 
	{                                                
		// 'this' doesn't have the mouse capture (most cases)                
		str.Format("%d, %d", point.x, point.y);                
		pMF->getStatusBar().SetPaneText(0,str);         
		// get the Status Bar and put text        
	} 
} 

void COpenGLView::rotate(int nValue) 
{
	double dbAngle = (8.0*atan(1.0))*m_dbMouseSensetivity*nValue;

	glMatrixMode(GL_MODELVIEW);        
	GLfloat transfMatrix[16];        
	glGetFloatv(GL_MODELVIEW_MATRIX,transfMatrix);        
	glLoadIdentity();               
	if (m_bIsObjectSpace == false) 
	{       
	  //world matrix should be multiplied from the left direction             
	  glRotated(dbAngle,(m_nAxis == ID_AXIS_X),(m_nAxis == ID_AXIS_Y),(m_nAxis == ID_AXIS_Z));                
	  glMultMatrixf(transfMatrix);        
	} 
	else 
	{                                                      
	  glMultMatrixf(transfMatrix);               
	  glRotatef(dbAngle,(m_nAxis == ID_AXIS_X),(m_nAxis == ID_AXIS_Y),(m_nAxis == ID_AXIS_Z));       
	} 
}

void COpenGLView::scale(int nValue) 
{
	if(nValue == 0 )
		return;
	
	double dbScale = 0.0;
	if(0.1*nValue*m_dbMouseSensetivity < -1)
	{
		dbScale = 0.2;
	}
	else
	{
		dbScale = 1 + 0.1*nValue*m_dbMouseSensetivity;
	}

	glMatrixMode(GL_MODELVIEW);        
	GLfloat transfMatrix[16];        
	glGetFloatv(GL_MODELVIEW_MATRIX,transfMatrix);        
	glLoadIdentity();  

	if(m_bIsObjectSpace == false)
	{
		//world matrix should be multiplied from the left direction             
		glScaled(dbScale,dbScale,dbScale);                
		glMultMatrixf(transfMatrix);        
	}
	else
	{
		glMultMatrixf(transfMatrix);     
		glScaled(dbScale,dbScale,dbScale);               
	}
}

void COpenGLView::translate(int nXValue) 
{
	double size = max(max(m_maxCoord.x - m_maxCoord.x,m_maxCoord.y-m_minCoord.y),
					  m_maxCoord.z - m_minCoord.z);

	double dbT = 0.0003*nXValue*m_dbMouseSensetivity*size;

	glMatrixMode(GL_MODELVIEW);        
	GLfloat transfMatrix[16];        
	glGetFloatv(GL_MODELVIEW_MATRIX,transfMatrix);        
	glLoadIdentity();  

	if(m_bIsObjectSpace == false)
	{
		//world matrix should be multiplied from the left direction             
		glTranslated((m_nAxis == ID_AXIS_X) ? dbT : 0, (m_nAxis == ID_AXIS_Y) ? dbT : 0, (m_nAxis == ID_AXIS_Z) ? dbT : 0);              
		glMultMatrixf(transfMatrix);        
	}
	else
	{
		glMultMatrixf(transfMatrix);   
		glTranslated((m_nAxis == ID_AXIS_X) ? dbT : 0, (m_nAxis == ID_AXIS_Y) ? dbT : 0, (m_nAxis == ID_AXIS_Z) ? dbT : 0);       
	}
}

void COpenGLView::SetLights (void) {
	glMatrixMode(GL_MODELVIEW);        
	GLfloat transfMatrix[16];        
	glGetFloatv(GL_MODELVIEW_MATRIX,transfMatrix);        
	glLoadIdentity();  

	/*
		GLfloat vec4 [4];
		GLfloat vec1 [] = { 32.0 };

		vec4[0] = m_nArrLightDirection[0]; 
		vec4[1] = m_nArrLightDirection[1]; 
		vec4[2] = m_nArrLightDirection[2]; 
		vec4[3] = 0;
		
		glLightfv(GL_LIGHT0, GL_POSITION, vec4);
		glLightfv(GL_LIGHT0, GL_SPOT_EXPONENT, vec1);
		vec4[0] = vec4[1] = vec4[2] = m_lMaterialAmbient; vec4[3] = 1;
		glLightfv(GL_LIGHT0, GL_AMBIENT, vec4);
		vec4[0] = vec4[1] = vec4[2] = m_lMaterialDiffuse;
		glLightfv(GL_LIGHT0, GL_DIFFUSE, vec4);
		vec4[0] = vec4[1] = vec4[2] = m_lMaterialSpecular;
		glLightfv(GL_LIGHT0, GL_SPECULAR, vec4);

		glEnable(GL_LIGHT0);
		glEnable(GL_LIGHTING);
	*/

	// ---------------
	// define lights
		GLfloat vec4 [4];
		GLfloat vec1 [] = { 32.0 };

		// light 0
		if (m_lightsDlg.m_light1enable) {
			vec4[0] = m_lightsDlg.m_light1x; 
			vec4[1] = m_lightsDlg.m_light1y; 
			vec4[2] = m_lightsDlg.m_light1z; 
			vec4[3] = m_lightsDlg.m_light1point;
			glLightfv(GL_LIGHT0, GL_POSITION, vec4);
			glLightfv(GL_LIGHT0, GL_SPOT_EXPONENT, vec1);
			vec4[0] = (m_lightsDlg.m_light1r / 255.0)*m_lMaterialAmbient; 
			vec4[1] = (m_lightsDlg.m_light1g / 255.0)*m_lMaterialAmbient; 
			vec4[2] = (m_lightsDlg.m_light1b / 255.0)*m_lMaterialAmbient; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT0, GL_AMBIENT, vec4);
			vec4[0] = (m_lightsDlg.m_light1r / 255.0)*m_lMaterialDiffuse; 
			vec4[1] = (m_lightsDlg.m_light1g / 255.0)*m_lMaterialDiffuse; 
			vec4[2] = (m_lightsDlg.m_light1b / 255.0)*m_lMaterialDiffuse; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT0, GL_DIFFUSE, vec4);
			vec4[0] = (m_lightsDlg.m_light1r / 255.0)*m_lMaterialSpecular;
			vec4[1] = (m_lightsDlg.m_light1g / 255.0)*m_lMaterialSpecular; 
			vec4[2] = (m_lightsDlg.m_light1b / 255.0)*m_lMaterialSpecular;
			vec4[3] = 1;
			glLightfv(GL_LIGHT0, GL_SPECULAR, vec4);
			glEnable(GL_LIGHT0);
		}
		else
			glDisable(GL_LIGHT0);

		// light 1
		if (m_lightsDlg.m_light2enable) {
			vec4[0] = m_lightsDlg.m_light2x; 
			vec4[1] = m_lightsDlg.m_light2y; 
			vec4[2] = m_lightsDlg.m_light2z; 
			vec4[3] = m_lightsDlg.m_light2point;
			glLightfv(GL_LIGHT1, GL_POSITION, vec4);
			glLightfv(GL_LIGHT1, GL_SPOT_EXPONENT, vec1);
			vec4[0] = (m_lightsDlg.m_light2r / 255.0)*m_lMaterialAmbient; 
			vec4[1] = (m_lightsDlg.m_light2g / 255.0)*m_lMaterialAmbient; 
			vec4[2] = (m_lightsDlg.m_light2b / 255.0)*m_lMaterialAmbient; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT1, GL_AMBIENT, vec4);
			vec4[0] = (m_lightsDlg.m_light2r / 255.0)*m_lMaterialDiffuse; 
			vec4[1] = (m_lightsDlg.m_light2g / 255.0)*m_lMaterialDiffuse; 
			vec4[2] = (m_lightsDlg.m_light2b / 255.0)*m_lMaterialDiffuse; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT1, GL_DIFFUSE, vec4);
			vec4[0] = (m_lightsDlg.m_light2r / 255.0)*m_lMaterialSpecular;
			vec4[1] = (m_lightsDlg.m_light2g / 255.0)*m_lMaterialSpecular; 
			vec4[2] = (m_lightsDlg.m_light2b / 255.0)*m_lMaterialSpecular;
			vec4[3] = 1;
			glLightfv(GL_LIGHT1, GL_SPECULAR, vec4);
			glEnable(GL_LIGHT1);
		}
		else
			glDisable(GL_LIGHT1);

		// light 2
		if (m_lightsDlg.m_light3enable) {
			vec4[0] = m_lightsDlg.m_light3x; 
			vec4[1] = m_lightsDlg.m_light3y; 
			vec4[2] = m_lightsDlg.m_light3z; 
			vec4[3] = m_lightsDlg.m_light3point;
			glLightfv(GL_LIGHT2, GL_POSITION, vec4);
			glLightfv(GL_LIGHT2, GL_SPOT_EXPONENT, vec1);
			vec4[0] = (m_lightsDlg.m_light3r / 255.0)*m_lMaterialAmbient; 
			vec4[1] = (m_lightsDlg.m_light3g / 255.0)*m_lMaterialAmbient; 
			vec4[2] = (m_lightsDlg.m_light3b / 255.0)*m_lMaterialAmbient; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT2, GL_AMBIENT, vec4);
			vec4[0] = (m_lightsDlg.m_light3r / 255.0)*m_lMaterialDiffuse; 
			vec4[1] = (m_lightsDlg.m_light3g / 255.0)*m_lMaterialDiffuse; 
			vec4[2] = (m_lightsDlg.m_light3b / 255.0)*m_lMaterialDiffuse; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT2, GL_DIFFUSE, vec4);
			vec4[0] = (m_lightsDlg.m_light3r / 255.0)*m_lMaterialSpecular;
			vec4[1] = (m_lightsDlg.m_light3g / 255.0)*m_lMaterialSpecular; 
			vec4[2] = (m_lightsDlg.m_light3b / 255.0)*m_lMaterialSpecular;
			vec4[3] = 1;
			glLightfv(GL_LIGHT2, GL_SPECULAR, vec4);
			glEnable(GL_LIGHT2);
		}
		else
			glDisable(GL_LIGHT2);

		// light 3
		if (m_lightsDlg.m_light4enable) {
			vec4[0] = m_lightsDlg.m_light4x; 
			vec4[1] = m_lightsDlg.m_light4y; 
			vec4[2] = m_lightsDlg.m_light4z; 
			vec4[3] = m_lightsDlg.m_light4point;
			glLightfv(GL_LIGHT3, GL_POSITION, vec4);
			glLightfv(GL_LIGHT3, GL_SPOT_EXPONENT, vec1);
			vec4[0] = (m_lightsDlg.m_light4r / 255.0)*m_lMaterialAmbient; 
			vec4[1] = (m_lightsDlg.m_light4g / 255.0)*m_lMaterialAmbient; 
			vec4[2] = (m_lightsDlg.m_light4b / 255.0)*m_lMaterialAmbient; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT3, GL_AMBIENT, vec4);
			vec4[0] = (m_lightsDlg.m_light4r / 255.0)*m_lMaterialDiffuse; 
			vec4[1] = (m_lightsDlg.m_light4g / 255.0)*m_lMaterialDiffuse; 
			vec4[2] = (m_lightsDlg.m_light4b / 255.0)*m_lMaterialDiffuse; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT3, GL_DIFFUSE, vec4);
			vec4[0] = (m_lightsDlg.m_light4r / 255.0)*m_lMaterialSpecular;
			vec4[1] = (m_lightsDlg.m_light4g / 255.0)*m_lMaterialSpecular; 
			vec4[2] = (m_lightsDlg.m_light4b / 255.0)*m_lMaterialSpecular;
			vec4[3] = 1;
			glLightfv(GL_LIGHT3, GL_SPECULAR, vec4);
			glEnable(GL_LIGHT3);
		}
		else
			glDisable(GL_LIGHT3);

		// light 4
		if (m_lightsDlg.m_light5enable) {
			vec4[0] = m_lightsDlg.m_light5x; 
			vec4[1] = m_lightsDlg.m_light5y; 
			vec4[2] = m_lightsDlg.m_light5z; 
			vec4[3] = m_lightsDlg.m_light5point;
			glLightfv(GL_LIGHT4, GL_POSITION, vec4);
			glLightfv(GL_LIGHT4, GL_SPOT_EXPONENT, vec1);
			vec4[0] = (m_lightsDlg.m_light5r / 255.0)*m_lMaterialAmbient; 
			vec4[1] = (m_lightsDlg.m_light5g / 255.0)*m_lMaterialAmbient; 
			vec4[2] = (m_lightsDlg.m_light5b / 255.0)*m_lMaterialAmbient; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT4, GL_AMBIENT, vec4);
			vec4[0] = (m_lightsDlg.m_light5r / 255.0)*m_lMaterialDiffuse; 
			vec4[1] = (m_lightsDlg.m_light5g / 255.0)*m_lMaterialDiffuse; 
			vec4[2] = (m_lightsDlg.m_light5b / 255.0)*m_lMaterialDiffuse; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT4, GL_DIFFUSE, vec4);
			vec4[0] = (m_lightsDlg.m_light5r / 255.0)*m_lMaterialSpecular;
			vec4[1] = (m_lightsDlg.m_light5g / 255.0)*m_lMaterialSpecular; 
			vec4[2] = (m_lightsDlg.m_light5b / 255.0)*m_lMaterialSpecular;
			vec4[3] = 1;
			glLightfv(GL_LIGHT4, GL_SPECULAR, vec4);
			glEnable(GL_LIGHT4);
		}
		else
			glDisable(GL_LIGHT4);

		// light 5
		if (m_lightsDlg.m_light6enable) {
			vec4[0] = m_lightsDlg.m_light6x; 
			vec4[1] = m_lightsDlg.m_light6y; 
			vec4[2] = m_lightsDlg.m_light6z; 
			vec4[3] = m_lightsDlg.m_light6point;
			glLightfv(GL_LIGHT5, GL_POSITION, vec4);
			glLightfv(GL_LIGHT5, GL_SPOT_EXPONENT, vec1);
			vec4[0] = (m_lightsDlg.m_light6r / 255.0)*m_lMaterialAmbient; 
			vec4[1] = (m_lightsDlg.m_light6g / 255.0)*m_lMaterialAmbient; 
			vec4[2] = (m_lightsDlg.m_light6b / 255.0)*m_lMaterialAmbient; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT5, GL_AMBIENT, vec4);
			vec4[0] = (m_lightsDlg.m_light6r / 255.0)*m_lMaterialDiffuse; 
			vec4[1] = (m_lightsDlg.m_light6g / 255.0)*m_lMaterialDiffuse; 
			vec4[2] = (m_lightsDlg.m_light6b / 255.0)*m_lMaterialDiffuse; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT5, GL_DIFFUSE, vec4);
			vec4[0] = (m_lightsDlg.m_light6r / 255.0)*m_lMaterialSpecular;
			vec4[1] = (m_lightsDlg.m_light6g / 255.0)*m_lMaterialSpecular; 
			vec4[2] = (m_lightsDlg.m_light6b / 255.0)*m_lMaterialSpecular;
			vec4[3] = 1;
			glLightfv(GL_LIGHT5, GL_SPECULAR, vec4);
			glEnable(GL_LIGHT5);
		}
		else
			glDisable(GL_LIGHT5);

		// light 6
		if (m_lightsDlg.m_light7enable) {
			vec4[0] = m_lightsDlg.m_light7x; 
			vec4[1] = m_lightsDlg.m_light7y; 
			vec4[2] = m_lightsDlg.m_light7z; 
			vec4[3] = m_lightsDlg.m_light7point;
			glLightfv(GL_LIGHT6, GL_POSITION, vec4);
			glLightfv(GL_LIGHT6, GL_SPOT_EXPONENT, vec1);
			vec4[0] = (m_lightsDlg.m_light7r / 255.0)*m_lMaterialAmbient; 
			vec4[1] = (m_lightsDlg.m_light7g / 255.0)*m_lMaterialAmbient; 
			vec4[2] = (m_lightsDlg.m_light7b / 255.0)*m_lMaterialAmbient; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT6, GL_AMBIENT, vec4);
			vec4[0] = (m_lightsDlg.m_light7r / 255.0)*m_lMaterialDiffuse; 
			vec4[1] = (m_lightsDlg.m_light7g / 255.0)*m_lMaterialDiffuse; 
			vec4[2] = (m_lightsDlg.m_light7b / 255.0)*m_lMaterialDiffuse; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT6, GL_DIFFUSE, vec4);
			vec4[0] = (m_lightsDlg.m_light7r / 255.0)*m_lMaterialSpecular;
			vec4[1] = (m_lightsDlg.m_light7g / 255.0)*m_lMaterialSpecular; 
			vec4[2] = (m_lightsDlg.m_light7b / 255.0)*m_lMaterialSpecular;
			vec4[3] = 1;
			glLightfv(GL_LIGHT6, GL_SPECULAR, vec4);
			glEnable(GL_LIGHT6);
		}
		else
			glDisable(GL_LIGHT6);

		// light 7
		if (m_lightsDlg.m_light8enable) {
			vec4[0] = m_lightsDlg.m_light8x; 
			vec4[1] = m_lightsDlg.m_light8y; 
			vec4[2] = m_lightsDlg.m_light8z; 
			vec4[3] = m_lightsDlg.m_light8point;
			glLightfv(GL_LIGHT7, GL_POSITION, vec4);
			glLightfv(GL_LIGHT7, GL_SPOT_EXPONENT, vec1);
			vec4[0] = (m_lightsDlg.m_light8r / 255.0)*m_lMaterialAmbient; 
			vec4[1] = (m_lightsDlg.m_light8g / 255.0)*m_lMaterialAmbient; 
			vec4[2] = (m_lightsDlg.m_light8b / 255.0)*m_lMaterialAmbient; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT7, GL_AMBIENT, vec4);
			vec4[0] = (m_lightsDlg.m_light8r / 255.0)*m_lMaterialDiffuse; 
			vec4[1] = (m_lightsDlg.m_light8g / 255.0)*m_lMaterialDiffuse; 
			vec4[2] = (m_lightsDlg.m_light8b / 255.0)*m_lMaterialDiffuse; 
			vec4[3] = 1;
			glLightfv(GL_LIGHT7, GL_DIFFUSE, vec4);
			vec4[0] = (m_lightsDlg.m_light8r / 255.0)*m_lMaterialSpecular;
			vec4[1] = (m_lightsDlg.m_light8g / 255.0)*m_lMaterialSpecular; 
			vec4[2] = (m_lightsDlg.m_light8b / 255.0)*m_lMaterialSpecular;
			vec4[3] = 1;
			glLightfv(GL_LIGHT7, GL_SPECULAR, vec4);
			glEnable(GL_LIGHT7);
		}
		else
			glDisable(GL_LIGHT7);
	// ---------------
	
	glMultMatrixf(transfMatrix);
}

void COpenGLView::OnSpaceObject()
{
	m_bIsObjectSpace = true;
}

void COpenGLView::OnUpdateSpaceObject(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bIsObjectSpace == true);
}

void COpenGLView::OnSpaceWorld()
{
	m_bIsObjectSpace = false;
}

void COpenGLView::OnUpdateSpaceWorld(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bIsObjectSpace == false);
}

void COpenGLView::DrawBoundingBox (void) {
	glBegin(GL_LINES);
		glColor3f(0,1,1); // yellow?
		glVertex3f(m_minCoord.x, m_minCoord.y, m_minCoord.z);
		glVertex3f(m_maxCoord.x, m_minCoord.y, m_minCoord.z);
		glVertex3f(m_minCoord.x, m_minCoord.y, m_minCoord.z);
		glVertex3f(m_minCoord.x, m_maxCoord.y, m_minCoord.z);
		glVertex3f(m_minCoord.x, m_minCoord.y, m_minCoord.z);
		glVertex3f(m_minCoord.x, m_minCoord.y, m_maxCoord.z);

		glVertex3f(m_maxCoord.x, m_maxCoord.y, m_maxCoord.z);
		glVertex3f(m_minCoord.x, m_maxCoord.y, m_maxCoord.z);
		glVertex3f(m_maxCoord.x, m_maxCoord.y, m_maxCoord.z);
		glVertex3f(m_maxCoord.x, m_minCoord.y, m_maxCoord.z);
		glVertex3f(m_maxCoord.x, m_maxCoord.y, m_maxCoord.z);
		glVertex3f(m_maxCoord.x, m_maxCoord.y, m_minCoord.z);

		glVertex3f(m_maxCoord.x, m_minCoord.y, m_minCoord.z);
		glVertex3f(m_maxCoord.x, m_minCoord.y, m_maxCoord.z);

		glVertex3f(m_maxCoord.x, m_minCoord.y, m_minCoord.z);
		glVertex3f(m_maxCoord.x, m_maxCoord.y, m_minCoord.z);

		glVertex3f(m_minCoord.x, m_maxCoord.y, m_minCoord.z);
		glVertex3f(m_minCoord.x, m_maxCoord.y, m_maxCoord.z);

		glVertex3f(m_minCoord.x, m_maxCoord.y, m_minCoord.z);
		glVertex3f(m_maxCoord.x, m_maxCoord.y, m_minCoord.z);

		glVertex3f(m_minCoord.x, m_minCoord.y, m_maxCoord.z);
		glVertex3f(m_minCoord.x, m_maxCoord.y, m_maxCoord.z);

		glVertex3f(m_minCoord.x, m_minCoord.y, m_maxCoord.z);
		glVertex3f(m_maxCoord.x, m_minCoord.y, m_maxCoord.z);
	glEnd();
}

void COpenGLView::DrawFaceNormals(VRIndexedFaceSet* ifs) {
	vector<MyNormal> &curr(GetFaceNormals(ifs));
	if (curr.empty())
		return;
	glBegin(GL_LINES);
	glColor3f(0,1,0); // green color
	unsigned j(0),poly(0);
	int idx;
	double spacing = 0.025 * 
			   (sqrt((m_maxCoord.x - m_minCoord.x)*(m_maxCoord.x - m_minCoord.x) + 
			   (m_maxCoord.y - m_minCoord.y) *(m_maxCoord.y - m_minCoord.y) +
			   (m_maxCoord.z - m_minCoord.z) *(m_maxCoord.z - m_minCoord.z)));
	while (j<ifs->coordIndex.numValues) 
	{
		MyPoint Center;
		int nNumPoints =0;
		while ((idx = ifs->coordIndex.values[j++]) != -1) 
		{
			Center.x += ifs->coord->point.values[idx][0];
			Center.y += ifs->coord->point.values[idx][1];
			Center.z += ifs->coord->point.values[idx][2];
			nNumPoints++;
		}
		glVertex3f(Center.x/nNumPoints, 
				   Center.y/nNumPoints, 
				   Center.z/nNumPoints);
		glVertex3f(Center.x/nNumPoints + spacing*curr[poly].x, 
				   Center.y/nNumPoints + spacing*curr[poly].y, 
				   Center.z/nNumPoints + spacing*curr[poly].z);
		
		++poly;
	}
	glEnd();
}

void COpenGLView::DrawVertexNormals(VRIndexedFaceSet* ifs) 
{
	vector<MyNormal> &curr(GetVertexNormals(ifs));
	if (curr.empty())
		return;
	glBegin(GL_LINES);
	glColor3f(0,0.5,1); // light blue color
	unsigned j(0);
	int idx;
	double spacing = 0.025 * 
			   (sqrt((m_maxCoord.x - m_minCoord.x)*(m_maxCoord.x - m_minCoord.x) + 
			   (m_maxCoord.y - m_minCoord.y) *(m_maxCoord.y - m_minCoord.y) +
			   (m_maxCoord.z - m_minCoord.z) *(m_maxCoord.z - m_minCoord.z)));

	while (j<ifs->coordIndex.numValues) 
	{
		while ((idx = ifs->coordIndex.values[j++]) != -1) 
		{
			glVertex3f(ifs->coord->point.values[idx][0], 
				       ifs->coord->point.values[idx][1], 
				       ifs->coord->point.values[idx][2]);
			glVertex3f(ifs->coord->point.values[idx][0] + spacing*curr[idx].x, 
				       ifs->coord->point.values[idx][1] + spacing*curr[idx].y, 
				       ifs->coord->point.values[idx][2] + spacing*curr[idx].z);
		}
		
	}
	glEnd();
}

void COpenGLView::OnNormalsFace()
{
	m_bDrawFaceNormals = !m_bDrawFaceNormals;
	Invalidate();
}

void COpenGLView::OnNormalsVertex()
{
	m_bDrawVertexNormals = !m_bDrawVertexNormals;
	Invalidate();
}

void COpenGLView::OnUpdateNormalsFace(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bDrawFaceNormals == true);
}

void COpenGLView::OnUpdateNormalsVertex(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bDrawVertexNormals == true);
}

void COpenGLView::OnOptionsBoundingbox()
{
	m_bDrawBoundingBox = ! m_bDrawBoundingBox;
	Invalidate();
}

void COpenGLView::OnUpdateOptionsBoundingbox(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bDrawVertexNormals == true);
}

void COpenGLView::DrawFog()
{				
	// Storage For Three Types Of Fog
	GLuint fogMode[]= { GL_EXP, GL_EXP2, GL_LINEAR };	
	// Which Fog To Use
	GLuint fogfilter= 2;					
	// Fog Color
	GLfloat fogColor[4]= {0.5f, 0.5f, 0.5f, 1.0f};
	
	// We'll Clear To The Color Of The Fog ( Modified )
	//glClearColor(0.5f,0.5f,0.5f,1.0f);			

	glFogi(GL_FOG_MODE, fogMode[fogfilter]);	// Fog Mode
	glFogfv(GL_FOG_COLOR, fogColor);			// Set Fog Color
	glFogf(GL_FOG_DENSITY, 0.05);				// How Dense Will The Fog Be
	glHint(GL_FOG_HINT, GL_NICEST);			// Fog Hint Value
	glFogf(GL_FOG_START, 1);					// Fog Start Depth
	glFogf(GL_FOG_END, 2);					// Fog End Depth
	glEnable(GL_FOG);							// Enables GL_FOG

	glFogi (GL_FOG_MODE, fogMode[m_FogFilter]);
	
	Invalidate();
}

void COpenGLView::OnOptionsAnti()
{
	m_bAntiAliasing = !m_bAntiAliasing;

	if (m_bAntiAliasing)
	{
		glEnable(GL_LINE_SMOOTH);
		glEnable(GL_POLYGON_SMOOTH);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ZERO);
	}
	else
	{
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_POLYGON_SMOOTH);
		glDisable(GL_BLEND);
	}
	Invalidate();
}

void COpenGLView::OnUpdateOptionsAnti(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bAntiAliasing == true);
}

void COpenGLView::SetView()
{
	double minX, maxX, minY, maxY, minZ, maxZ;

	minX = m_minCoord.x;
	maxX = m_maxCoord.x;
	minY = m_minCoord.y;
	maxY = m_maxCoord.y;
	minZ = m_minCoord.z;
	maxZ = m_maxCoord.z;

	double spacing = 
		0.05*(sqrt((maxX-minX)*(maxX-minX) + (maxY-minY)*(maxY-minY) + (maxZ-minZ)*(maxZ-minZ)));

	double factor = spacing;

	minX -= (factor);
	maxX += (factor);
	minY -= (factor);
	maxY += (factor);
	minZ -= (factor)*2000;
	maxZ += (factor)*2000;

	double Xsize = maxX - minX;
	double Ysize = maxY - minY;
	double M;

	if (Xsize / Ysize < m_AspectRatio)
	{
		M = m_AspectRatio * Ysize / Xsize;
		factor = M * Xsize;
		minX += Xsize / 2 - factor /2;
		maxX += factor / 2 - Xsize / 2;
	}
	else
	{
		M = Xsize / (Ysize * m_AspectRatio);
		factor = M * Ysize;
		minY += Ysize / 2 - factor / 2;
		maxY += factor / 2 - Ysize / 2;
	}

	if (!m_bIsPerspective)
	{
		GLfloat transformTemp[16];
		
		glGetFloatv(GL_MODELVIEW_MATRIX,transformTemp);
		glLoadIdentity();
		gluLookAt(0,0,0,0,0,-1,0,1,0);
		glMultMatrixf(transformTemp);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(minX,maxX,minY,maxY,minZ,maxZ);
	}
	else
	{
		minX = m_minCoord.x;
		maxX = m_maxCoord.x;
		minY = m_minCoord.y;
		maxY = m_maxCoord.y;
		minZ = m_minCoord.z;
		maxZ = m_maxCoord.z;

		GLfloat transformTemp[16];
		glPopMatrix();
		glGetFloatv(GL_MODELVIEW_MATRIX,transformTemp);
		glPushMatrix();
		glLoadIdentity();
		gluLookAt((minX + maxX)/2, 
				  (minY + maxY)/2, (maxZ > 0)?maxZ + 10 * spacing:spacing, 
				  (minX + maxX)/2, (minY + maxY)/2, minZ, 0,1,0);
		glMultMatrixf(transformTemp);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60,m_AspectRatio,(maxZ > 0)?maxZ :0 ,minZ);
	}

	glMatrixMode(GL_MODELVIEW);
}
void COpenGLView::OnOptionsBackgroundimage()
{

	//	Reading Background from file 
	if (m_bBackgroundImage) {	// if active, cancel it
		m_bBackgroundImage = false;
	}
	else {		// retrieve image, and check if successful
		glReadBuffer(GL_BACK);

		TCHAR szFilters[] = _T ("PNG Image Files (*.png)|*.png|All Files (*.*)|*.*||");

		CFileDialog dlg(TRUE, "png", "*.png", OFN_FILEMUSTEXIST | OFN_HIDEREADONLY ,szFilters);

		if (dlg.DoModal () == IDOK) {		// display modal window
			m_strBackImage = dlg.GetPathName();		// Get full path and filename
			// Use this name when rendering. Open it and read it.

			m_bBackgroundImage = true;
			Invalidate();
		} 
	}	
}

void COpenGLView::OnUpdateOptionsBackgroundimage(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bBackgroundImage==true);
}

void COpenGLView::OnFileSave()
{
	m_bSaveToFile = true;
	Invalidate();
}

void COpenGLView::OnOptionsMousesensitivity()
{
	MouseSensDlg dlg;
	dlg.m_mouseSens = m_dbMouseSensetivity / 2 * 100;
	if (dlg.DoModal() == IDOK)
		m_dbMouseSensetivity = (float) dlg.m_mouseSens / 100 * 2;
}

void COpenGLView::OnLightFogsetting()
{
	FogSetupDlg dlg;

	dlg.m_boolFirstRadio = m_fogMode;

	dlg.m_r = m_fogColor.r * 255;
	dlg.m_g = m_fogColor.g * 255;
	dlg.m_b = m_fogColor.b * 255;

	dlg.m_density = m_fogDensity;
	dlg.m_start = m_fogStart;
	dlg.m_end = m_fogEnd;

	dlg.m_enable = m_fogEnable;

	if (dlg.DoModal() == IDOK) {

		m_fogMode = dlg.m_boolFirstRadio;

		m_fogColor.r = (float) dlg.m_r / 255;
		m_fogColor.g = (float) dlg.m_g / 255;
		m_fogColor.b = (float) dlg.m_b / 255;

		m_fogDensity = dlg.m_density;
		m_fogStart = dlg.m_start;
		m_fogEnd = dlg.m_end;

		m_fogEnable = dlg.m_enable;

		// ----

		if (dlg.m_boolFirstRadio == 0)
			glFogi(GL_FOG_MODE, GL_EXP);
		else if (dlg.m_boolFirstRadio == 1)
			glFogi(GL_FOG_MODE, GL_EXP2);
		else 
			glFogi(GL_FOG_MODE, GL_LINEAR);
					
		float r((float) dlg.m_r / 255), g((float) dlg.m_g / 255), b((float) dlg.m_b / 255);
		GLfloat fogColor[4]= {r, g, b, 1.0f};
		glFogfv(GL_FOG_COLOR, fogColor);	

		glFogf(GL_FOG_DENSITY, dlg.m_density);

		glHint(GL_FOG_HINT, GL_NICEST);			// Fog Hint Value

		glFogf(GL_FOG_START, dlg.m_start);
		glFogf(GL_FOG_END, dlg.m_end);

		if (dlg.m_enable)
			glEnable(GL_FOG);
		else
			glDisable(GL_FOG);
		
		Invalidate();
	}
}
